#include "struct.h"

void error_exit(const char* errorname)
{
    printf("%s:%m\n",errorname);
    exit(EXIT_FAILURE);
}

int out_struct(const void* ptr,FILE* fp)
{
    int ret = fprintf(fp,"ID:%u\n用户名:%s  密码:%s\n金额:%.2f 锁定状态:%s 剩余密码错误机会:%u"
    ,((Accont*)ptr)->id,((Accont*)ptr)->name,((Accont*)ptr)->pswd,((Accont*)ptr)->balance,
    ((Accont*)ptr)->flag_lock ? "锁定":"未锁定",((Accont*)ptr)->lock_times);
    return ret;
}

void open_accont(int cli_fd,Accont* acc)
{
    get_id(PATH_DATA,&(acc->id));
    acc->flag_lock = 0;
    acc->lock_times = 3;
    
    char path[30];
    sprintf(path,"%s%u",PATH_ACC,acc->id);

    char buf[50];
    if(save_text(acc,sizeof(Accont),1,path,out_struct))
    {
        sprintf(buf,"%u用户开户成功!\n",acc->id);
        printf("%u用户开户成功!\n",acc->id);
    }
    else
    {
        strcpy(buf,"开户失败!请联系管理员!");
        printf("%u用户开户失败!\n",acc->id);
    }
    send(cli_fd, buf, strlen(buf) + 1, 0);
}

void *run(void *arg)
{
    int cli_fd = *(int *)arg;
    Client_Msg msg;
    char* stoc = (char*)&msg;

    for(;;)
    {
        // 接收数据
        int sum = sizeof(Client_Msg);
        int i=0;
        do{
            recv(cli_fd,&stoc[i],1,0);      //接受到标识符\n停止,并将\n赋值为0作为字符串结束标志位
        }while('\n' != stoc[i++]);
        stoc[i-1] = '\0';

        char *tmp = stoc+i;                 //接受余下数据 防止后面接收到以前的数据残留
        recv(cli_fd,tmp,sum-i,0);

        if (0 >= msg.type || 9<msg.type)
        {
            break;
        }

        //业务判断
        if(ACCONT_OPEN == msg.type)
        {
            open_accont(cli_fd,&(msg.acc));
        }
        else if(ACCONT_SAVE == msg.type)
        {
            save_accont(cli_fd,&msg);
        }
        else if(ACCONT_TAKE == msg.type)
        {
            take_accont(cli_fd,&msg);
        }
        else if(ACCONT_QUERY == msg.type)
        {
            //query_accont(cli_fd,&(msg.acc));
        }
        else if(ACCONT_LOGIN == msg.type)
        {
            login_accont(cli_fd,&(msg.acc));
        }
        else if(ACCONT_TRANS == msg.type)
        {
            trans_accont(cli_fd,&msg);
        }
        else if(ACCONT_UNLOCK == msg.type)
        {
            unlock_accont(cli_fd,&(msg.acc));
        }
        else if(ACCONT_MODIFY == msg.type)
        {
            modify_accont(cli_fd,&(msg.acc));
        }
        else if(ACCONT_DESTROY == msg.type)
        {
            destroy_accont(cli_fd,&(msg.acc));
        }
    }

    printf("通信结束！\n");
    // 关闭通信socket
    close(cli_fd);
}



int main(int argc,const char* argv[])
{
    int svr_fd = socket(AF_INET,SOCK_STREAM,0);
    init_id(PATH_DATA,202200001);
    struct sockaddr_in addr = {};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(2323);
    // 点分十进制字符串 -> 网络字节序32位无符号整数
    addr.sin_addr.s_addr = inet_addr("10.0.2.15");
    socklen_t addrlen = sizeof(addr);

    //绑定socket对象和通信地址
    if(bind(svr_fd,(struct sockaddr*)&addr,addrlen))
    {
        error_exit("bind");
    }

    if(listen(svr_fd,3))
    {
        error_exit("listen");
    }

    for(;;)
    {
        int cli_fd = accept(svr_fd,NULL,NULL);
        if(-1 == cli_fd)
        {
            error_exit("accept");
        }

        pthread_t tid;
        pthread_create(&tid,NULL,run,&cli_fd);
        usleep(1000);
    }
}

int in_struct(void* ptr,FILE* fp)
{
    char str[15];
    int ret = fscanf(fp,"ID:%u\n用户名:%s  密码:%s\n金额:%f 锁定状态:%s 剩余密码错误机会:%u"
    ,&(((Accont*)ptr)->id),((Accont*)ptr)->name,((Accont*)ptr)->pswd,&(((Accont*)ptr)->balance),
    str,&(((Accont*)ptr)->lock_times));
    if(0 == strcmp(str,"未锁定"))
    {
        ((Accont*)ptr)->flag_lock = 0;
    }
    else
    {
        ((Accont*)ptr)->flag_lock = 1;
    }
    return ret;
}

void login_accont(int cli_fd,Accont* acc)
{
    char path[50];
    sprintf(path,"%s%u",PATH_ACC,acc->id);

    Accont real_acc;
    int ret = load_text(&real_acc,sizeof(Accont),1,path,in_struct);

    char buf[50];
    if(0 == ret)
    {
        sprintf(buf,"%u没有该账号存在!\n",acc->id);
        printf("%s\n",buf);
        send(cli_fd, buf, strlen(buf) + 1, 0);
        return;
    }
    if(1 == real_acc.flag_lock)
    {
        sprintf(buf,"该账号已被锁定,无法登录!");
        send(cli_fd, buf, strlen(buf) + 1, 0);
        return;
    }
    if(strcmp(acc->pswd,real_acc.pswd))
    {
        sprintf(buf,"密码错误!您还有%u次机会",--(real_acc.lock_times));
        if(0 >= real_acc.lock_times)
        {
            real_acc.lock_times = 0;
            real_acc.flag_lock = 1;
            printf("%u 账户已被锁定!\n",real_acc.id);
        }
        save_text(&real_acc,sizeof(Accont),1,path,out_struct);
        printf("%u %s",acc->id,buf);
        send(cli_fd, buf, strlen(buf) + 1, 0);
        return;
    }

    sprintf(buf,"登录成功!");
    printf("%u %s\n",acc->id,buf);
    real_acc.lock_times = 3;
    save_text(&real_acc,sizeof(Accont),1,path,out_struct);
    send(cli_fd, buf, strlen(buf) + 1, 0);
    usleep(1000);
    send(cli_fd,&real_acc,sizeof(Accont),0);
}

void save_accont(int cli_fd,Client_Msg* msg)
{
    char path[50];
    sprintf(path,"%s%u",PATH_ACC,msg->acc.id);
    int cnt = save_text(&(msg->acc),sizeof(Accont),1,path,out_struct);
    
    char buf[30];
    if(0 == cnt)
    {
        sprintf(buf,"存钱失败!");
    }
    else
    {
        sprintf(buf,"存钱成功!");
    }
    send(cli_fd, buf, strlen(buf) + 1, 0);
    printf("%u 存 %s %s\n",msg->acc.id,msg->buf,buf);
}

void take_accont(int cli_fd,Client_Msg* msg)
{
    char path[50];
    sprintf(path,"%s%u",PATH_ACC,msg->acc.id);
    int cnt = save_text(&(msg->acc),sizeof(Accont),1,path,out_struct);
    
    char buf[30];
    if(0 == cnt)
    {
        sprintf(buf,"取钱失败!");
    }
    else
    {
        sprintf(buf,"取钱成功!");
    }
    send(cli_fd, buf, strlen(buf) + 1, 0);
    printf("%u 取 %s %s\n",msg->acc.id,msg->buf,buf);
}

void get_idcash(char* buf,size_t *id,float* cash)
{
    char str1[20],str2[20];
    char *tmp = buf + 9;

    strncpy(str1,buf,9);
    str1[9] = '\0';
    strcpy(str2,tmp);

    *id = (size_t)atoi(str1);
    *cash = (float)(atof(str2));
}

void trans_accont(int cli_fd,Client_Msg* msg)
{
    //对发送来的转账账号和钱进行处理
    size_t id;
    float cash;
    get_idcash(msg->buf,&id,&cash);
    

    char buf[100];
    char path[50];
    sprintf(path,"%s%u",PATH_ACC,msg->acc.id);
    int cnt = save_text(&(msg->acc),sizeof(Accont),1,path,out_struct);
    if(0 == cnt)
    {
        sprintf(buf,"转账失败!");
        printf("%s\n",buf);
        send(cli_fd, buf, strlen(buf) + 1, 0);
        return;
    }

    Accont acc;
    memset(path,0,strlen(path));
    sprintf(path,"%s%u",PATH_ACC,id);
    cnt = load_text(&acc,sizeof(Accont),1,path,in_struct);
    if(0 == cnt)
    {
        sprintf(buf,"%u没有该账号!",id);
        printf("%s\n",buf);
        send(cli_fd, buf, strlen(buf) + 1, 0);
        return;
    }

    acc.balance += cash;
    cnt = save_text(&acc,sizeof(Accont),1,path,out_struct);
    
    sprintf(buf,"%u 向 %u 转账 %.2f元",msg->acc.id,id,cash);
    printf("%s\n",buf);
    send(cli_fd, buf, strlen(buf) + 1, 0);
}

void modify_accont(int cli_fd,Accont* acc)
{
    char buf[30];
    char path[50];
    sprintf(path,"%s%u",PATH_ACC,acc->id);
    
    int cnt = save_text(acc,sizeof(Accont),1,path,out_struct);
    if(0 == cnt)
    {
        sprintf(buf,"改密码失败!");
        printf("%u %s\n",acc->id,buf);
        send(cli_fd, buf, strlen(buf) + 1, 0);
        return;
    }
    sprintf(buf,"改密码成功!");
    printf("%u %s\n",acc->id,buf);
    send(cli_fd, buf, strlen(buf) + 1, 0);
}

void destroy_accont(int cli_fd,Accont* acc)
{
    char buf[30];
    char path[50];
    sprintf(path,"%s%u",PATH_ACC,acc->id);

    if(remove(path))
    {
        sprintf(buf,"服务器升级中!请稍后再试");
        printf("%u 销户失败\n",acc->id);
        send(cli_fd, buf, strlen(buf) + 1, 0);
        return;
    }

    sprintf(buf,"销户成功!");
    printf("%u %s\n",acc->id,buf);
    send(cli_fd, buf, strlen(buf) + 1, 0);
}

void unlock_accont(int cli_fd,Accont* acc)
{
    char path[50];
    sprintf(path,"%s%u",PATH_ACC,acc->id);

    Accont real_acc;
    int ret = load_text(&real_acc,sizeof(Accont),1,path,in_struct);

    char buf[50];
    if(0 == ret)
    {
        sprintf(buf,"没有该账号存在!");
        printf("%s\n",buf);
        send(cli_fd, buf, strlen(buf) + 1, 0);
        return;
    }

    if(0 == strcmp(acc->name,real_acc.name) && 0 == strcmp(acc->pswd,real_acc.pswd))
    {
        sprintf(buf,"解锁成功");
        printf("%u %s\n",acc->id,buf);
        real_acc.flag_lock = 0;
        real_acc.lock_times = 3;
        save_text(&real_acc,sizeof(Accont),1,path,out_struct);
        send(cli_fd, buf, strlen(buf) + 1, 0);
        return;
    }

    sprintf(buf,"解锁失败");
    printf("%u %s\n",acc->id,buf);
    send(cli_fd, buf, strlen(buf) + 1, 0);
}